package org.keyczar.rsa;

import org.keyczar.exceptions.KeyczarException;
import org.keyczar.interfaces.DecryptingStream;
import org.keyczar.interfaces.EncryptingStream;
import org.keyczar.interfaces.SigningStream;
import org.keyczar.interfaces.VerifyingStream;

import java.nio.ByteBuffer;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.Signature;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;

import javax.crypto.Cipher;
import javax.crypto.ShortBufferException;

/**
 * Stream implementation used by {@link RsaPrivateKey}.
 */
class RsaDecryptingStream extends RsaSigningStream implements DecryptingStream, EncryptingStream {
  private final Cipher cipher;
  private final EncryptingStream encryptingStream;
  private final int digestSize;
  
  public RsaDecryptingStream(Signature signature, Cipher cipher, 
      RSAPrivateKey jcePrivateKey, RSAPublicKey jcePublicKey) {
    super(signature, jcePrivateKey, jcePublicKey);
    this.cipher = cipher;
    this.encryptingStream = new RsaEncryptingStream(signature, cipher, jcePublicKey);
    this.digestSize = jcePublicKey.getModulus().bitLength() / 8;
  }
  
  @Override
  public int doFinalDecrypt(ByteBuffer input, ByteBuffer output)
      throws KeyczarException {
    try {
      return cipher.doFinal(input, output);
    } catch (GeneralSecurityException e) {
      throw new KeyczarException(e);
    }
  }
  
  @Override
  public VerifyingStream getVerifyingStream() {
    return new VerifyingStream() {
      @Override
      public int digestSize() {
        return 0;
      }

      @Override
      public void initVerify() {
        // Do nothing
      }

      @Override
      public void updateVerify(ByteBuffer input) {
        // Do nothing
      }

      @Override
      public boolean verify(ByteBuffer signature) {
        // Do nothing
        return true;
      }
    };
  }
  
  @Override
  public int initEncrypt(ByteBuffer output) throws KeyczarException {
    return encryptingStream.initEncrypt(output);
  }
  
  @Override
  public void initDecrypt(ByteBuffer input) throws KeyczarException {
    try {
      cipher.init(Cipher.DECRYPT_MODE, jcePrivateKey);
    } catch (InvalidKeyException e) {
      throw new KeyczarException(e);
    }
  }
  
  @Override
  public int updateDecrypt(ByteBuffer input, ByteBuffer output)
      throws KeyczarException {
    try {
      return cipher.update(input, output);
    } catch (ShortBufferException e) {
      throw new KeyczarException(e);
    }
  }

  @Override
  public int doFinalEncrypt(ByteBuffer input, ByteBuffer output)
      throws KeyczarException {
    return encryptingStream.doFinalEncrypt(input, output);
  }

  @Override
  public SigningStream getSigningStream() throws KeyczarException {
    return encryptingStream.getSigningStream();
  }

  @Override
  public int maxOutputSize(int inputLen) {
    return digestSize;
  }
  
  @Override
  public int updateEncrypt(ByteBuffer input, ByteBuffer output)
      throws KeyczarException {
    return encryptingStream.updateEncrypt(input, output);
  }
}
